% arrow.tex: macros for commutative diagrams.
% 
% Copyright (C) 1991,1992 Steven T. Smith.
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2, or (at your option)
% any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

% Version 0   Released for alpha testing, November 16, 1991.
% Version 0.1 Morphism positions for slanted lines improved, Nov. 17, 1991.
% Version 0.11 \biline changed to \bisline for compatible naming. 11/21/91
% Version 0.2 Equate \lft & \rt w. _ and ^ instead of L_{12} & R_{12}. 1/20/92
% Version 1.0 Distributed with Eplain. 1/20/92
% Version 1.1 Purged \newcount's, etc.; Warner ref.; \getch@nnel logic. 4/21/92


% Syntax: \[arrow](X,Y) or
%         \[arrow](X,Y)\lft{MOR} (morphism placed left of arrow) or
%         \[arrow](X,Y)\rt{MOR} (morphism placed right of arrow)
%
% [arrow] is one of
%    sline     (straight line)
%    dotline   (dotted line)             * Unimplemented
%    arrow     (straight arrow)
%    dotarrow  (dotted arrow)            * Unimplemented
%
% The following allow plain versions and some combination of \lft and \rt.
%    biarrow   (two straight arrows)
%    adjarrow  (two adjoint arrows)
%    bisline   (two straight lines)
%
% Also, for left, right, up, and down mappings:
%
% \mapright     (or \mapright^{f_*}, \mapright_{f_*})
% \mapleft      (ditto)
% \mapup        (use \rt and \lft as above)
% \mapdown      (ditto)
%
% And variants of these (can use combinations of ^/_ and lft/rt):
%
% \bimapright   (two right arrows)
% \bimapleft    (two left arrows)
% \adjmapright  (two adjoint arrows; <- over ->)
% \adjmapleft   (two adjoint arrows; -> over <-)
% \hline        (horizontal line)
% \dothline     (dotted horizontal line)     * Unimplemented
% \bihline      (two horizontal lines)
%
% \bimapdown    (two down arrows)
% \bimapup      (two left arrows)
% \adjmapdown   (two adjoint arrows; down then up)
% \adjmapup     (two adjoint arrows; up then down)
% \vline        (vertical line)
% \dotvline     (dotted vertical line)       * Unimplemented
% \bivline      (two vertical lines)

% Use \thinlines temporarily to find the current catcode of @, so we can
% restore it at the end.
\edef\thinlines{\the\catcode`@ }%
\catcode`@ = 11
\let\@oldatcatcode = \thinlines

% Adapted LaTeX code for drawing lines and vectors

% Note: to ensure compatibility with LaTeX, all LaTeX control
% sequences have been renamed.  Control sequence names containing the
% at sign (@) have been changed to contain an ampersand (&) instead.
\edef\@oldandcatcode{\the\catcode`& }%
\catcode`& = 11

% LaTeX macros changed here:
% \line - changed to \drawline
% \vector - changed to \drawvector
% \@badlinearg - simply uses \errmessage now
% \@height, \@width, and \@depth are changed to height, width, and depth
% \@sline and \@svector - changed so that \hbox{\drawline...} yields
%  a box of positive width and positive height for a positive slope
%  and positive depth for a negative slope.
% \@hline and \@hvector - likewise
% \unitlength eliminated; pass dimensions to \drawline and \drawvector.

% LaTeX's while loop
\def\&whilenoop#1{}%
\def\&whiledim#1\do #2{\ifdim #1\relax#2\&iwhiledim{#1\relax#2}\fi}%
\def\&iwhiledim#1{\ifdim #1\let\&nextwhile=\&iwhiledim 
        \else\let\&nextwhile=\&whilenoop\fi\&nextwhile{#1}}%

% LaTeX's \line and \vector macros:
\newif\if&negarg
\newdimen\&wholewidth
\newdimen\&halfwidth

\font\tenln=line10

\def\thinlines{\let\&linefnt\tenln \let\&circlefnt\tencirc
  \&wholewidth\fontdimen8\tenln \&halfwidth .5\&wholewidth}%
\def\thicklines{\let\&linefnt\tenlnw \let\&circlefnt\tencircw
  \&wholewidth\fontdimen8\tenlnw \&halfwidth .5\&wholewidth}%

\def\drawline(#1,#2)#3{\&xarg #1\relax \&yarg #2\relax \&linelen=#3\relax
  \ifnum\&xarg =0 \&vline \else \ifnum\&yarg =0 \&hline \else \&sline\fi\fi}%

\def\&sline{\leavevmode
  \ifnum\&xarg< 0 \&negargtrue \&xarg -\&xarg \&yyarg -\&yarg
  \else \&negargfalse \&yyarg \&yarg \fi
  \ifnum \&yyarg >0 \&tempcnta\&yyarg \else \&tempcnta -\&yyarg \fi
  \ifnum\&tempcnta>6 \&badlinearg \&yyarg0 \fi
  \ifnum\&xarg>6 \&badlinearg \&xarg1 \fi
  \setbox\&linechar\hbox{\&linefnt\&getlinechar(\&xarg,\&yyarg)}%
  \ifnum \&yyarg >0 \let\&upordown\raise \&clnht\z@
  \else\let\&upordown\lower \&clnht \ht\&linechar\fi
  \&clnwd=\wd\&linechar
  \&whiledim \&clnwd <\&linelen \do {%
    \&upordown\&clnht\copy\&linechar
    \advance\&clnht \ht\&linechar
    \advance\&clnwd \wd\&linechar
  }%
  \advance\&clnht -\ht\&linechar
  \advance\&clnwd -\wd\&linechar
  \&tempdima\&linelen\advance\&tempdima -\&clnwd
  \&tempdimb\&tempdima\advance\&tempdimb -\wd\&linechar
  \hskip\&tempdimb \multiply\&tempdima \@m
  \&tempcnta \&tempdima \&tempdima \wd\&linechar \divide\&tempcnta \&tempdima
  \&tempdima \ht\&linechar \multiply\&tempdima \&tempcnta
  \divide\&tempdima \@m
  \advance\&clnht \&tempdima
  \ifdim \&linelen <\wd\&linechar \hskip \wd\&linechar
  \else\&upordown\&clnht\copy\&linechar\fi}%

\def\&hline{\vrule height \&halfwidth depth \&halfwidth width \&linelen}%

\def\&getlinechar(#1,#2){\&tempcnta#1\relax\multiply\&tempcnta 8
  \advance\&tempcnta -9 \ifnum #2>0 \advance\&tempcnta #2\relax\else
  \advance\&tempcnta -#2\relax\advance\&tempcnta 64 \fi
  \char\&tempcnta}%

\def\drawvector(#1,#2)#3{\&xarg #1\relax \&yarg #2\relax
  \&tempcnta \ifnum\&xarg<0 -\&xarg\else\&xarg\fi
  \ifnum\&tempcnta<5\relax \&linelen=#3\relax
    \ifnum\&xarg =0 \&vvector \else \ifnum\&yarg =0 \&hvector
    \else \&svector\fi\fi\else\&badlinearg\fi}%

\def\&hvector{\ifnum\&xarg<0 \rlap{\&linefnt\&getlarrow(1,0)}\fi \&hline
  \ifnum\&xarg>0 \llap{\&linefnt\&getrarrow(1,0)}\fi}%

\def\&vvector{\ifnum \&yarg <0 \&downvector \else \&upvector \fi}%

\def\&svector{\&sline
  \&tempcnta\&yarg \ifnum\&tempcnta <0 \&tempcnta=-\&tempcnta\fi
  \ifnum\&tempcnta <5 
    \if&negarg\ifnum\&yarg>0                   % 3d quadrant; dp > 0
      \llap{\lower\ht\&linechar\hbox to\&linelen{\&linefnt
        \&getlarrow(\&xarg,\&yyarg)\hss}}\else % 4th quadrant; ht > 0
      \llap{\hbox to\&linelen{\&linefnt\&getlarrow(\&xarg,\&yyarg)\hss}}\fi
    \else\ifnum\&yarg>0                        % 1st quadrant; ht > 0
      \&tempdima\&linelen \multiply\&tempdima\&yarg
      \divide\&tempdima\&xarg \advance\&tempdima-\ht\&linechar
      \raise\&tempdima\llap{\&linefnt\&getrarrow(\&xarg,\&yyarg)}\else
      \&tempdima\&linelen \multiply\&tempdima-\&yarg % 2d quadrant; dp > 0
      \divide\&tempdima\&xarg
      \lower\&tempdima\llap{\&linefnt\&getrarrow(\&xarg,\&yyarg)}\fi\fi
  \else\&badlinearg\fi}%

\def\&getlarrow(#1,#2){\ifnum #2 =\z@ \&tempcnta='33\else
\&tempcnta=#1\relax\multiply\&tempcnta \sixt@@n \advance\&tempcnta
-9 \&tempcntb=#2\relax\multiply\&tempcntb \tw@
\ifnum \&tempcntb >0 \advance\&tempcnta \&tempcntb\relax
\else\advance\&tempcnta -\&tempcntb\advance\&tempcnta 64
\fi\fi\char\&tempcnta}%

\def\&getrarrow(#1,#2){\&tempcntb=#2\relax
\ifnum\&tempcntb < 0 \&tempcntb=-\&tempcntb\relax\fi
\ifcase \&tempcntb\relax \&tempcnta='55 \or 
\ifnum #1<3 \&tempcnta=#1\relax\multiply\&tempcnta
24 \advance\&tempcnta -6 \else \ifnum #1=3 \&tempcnta=49
\else\&tempcnta=58 \fi\fi\or 
\ifnum #1<3 \&tempcnta=#1\relax\multiply\&tempcnta
24 \advance\&tempcnta -3 \else \&tempcnta=51\fi\or 
\&tempcnta=#1\relax\multiply\&tempcnta
\sixt@@n \advance\&tempcnta -\tw@ \else
\&tempcnta=#1\relax\multiply\&tempcnta
\sixt@@n \advance\&tempcnta 7 \fi\ifnum #2<0 \advance\&tempcnta 64 \fi
\char\&tempcnta}%

\def\&vline{\ifnum \&yarg <0 \&downline \else \&upline\fi}%

\def\&upline{\hbox to \z@{\hskip -\&halfwidth \vrule width \&wholewidth
   height \&linelen depth \z@\hss}}%

\def\&downline{\hbox to \z@{\hskip -\&halfwidth \vrule width \&wholewidth
   height \z@ depth \&linelen \hss}}%

\def\&upvector{\&upline\setbox\&tempboxa\hbox{\&linefnt\char'66}\raise 
     \&linelen \hbox to\z@{\lower \ht\&tempboxa\box\&tempboxa\hss}}%

\def\&downvector{\&downline\lower \&linelen
      \hbox to \z@{\&linefnt\char'77\hss}}%

\def\&badlinearg{\errmessage{Bad \string\arrow\space argument.}}%

%INITIALIZATION
\thinlines

% Allocate registers using the rules of p.~346 of {\sl The \TeX book}.
\countdef\&xarg     0
\countdef\&yarg     2
\countdef\&yyarg    4
\countdef\&tempcnta 6
\countdef\&tempcntb 8

\dimendef\&linelen  0
\dimendef\&clnwd    2
\dimendef\&clnht    4
\dimendef\&tempdima 6
\dimendef\&tempdimb 8

\chardef\@arrbox    0
\chardef\&linechar  2
\chardef\&tempboxa  2           % \&linechar and \&tempboxa don't interfere.


% Macros for abstract nonsense

% Macros for slanted lines and arrows.

\let\lft^%
\let\rt_% distinguish between \rt and \lft

\newif\if@pslope % test for positive slope
\def\@findslope(#1,#2){\ifnum#1>0
  \ifnum#2>0 \@pslopetrue \else\@pslopefalse\fi \else
  \ifnum#2>0 \@pslopefalse \else\@pslopetrue\fi\fi}%

\def\generalsmap(#1,#2){\getm@rphposn(#1,#2)\plnmorph\futurelet\next\addm@rph}%

% Put arrow in \@arrbox, then add morphisms later.

% Single lines and arrows.
\def\sline(#1,#2){\setbox\@arrbox=\hbox{\drawline(#1,#2){\sarrowlength}}%
  \@findslope(#1,#2)\d@@blearrfalse\generalsmap(#1,#2)}%
\def\arrow(#1,#2){\setbox\@arrbox=\hbox{\drawvector(#1,#2){\sarrowlength}}%
  \@findslope(#1,#2)\d@@blearrfalse\generalsmap(#1,#2)}%

% Double lines, arrows, and adjoint arrows.
\newif\ifd@@blearr

\def\bisline(#1,#2){\@findslope(#1,#2)%
  \if@pslope \let\@upordown\raise \else \let\@upordown\lower\fi
  \getch@nnel(#1,#2)\setbox\@arrbox=\hbox{\@upordown\@vchannel
    \rlap{\drawline(#1,#2){\sarrowlength}}%
      \hskip\@hchannel\hbox{\drawline(#1,#2){\sarrowlength}}}%
  \d@@blearrtrue\generalsmap(#1,#2)}%
\def\biarrow(#1,#2){\@findslope(#1,#2)%
  \if@pslope \let\@upordown\raise \else \let\@upordown\lower\fi
  \getch@nnel(#1,#2)\setbox\@arrbox=\hbox{\@upordown\@vchannel
    \rlap{\drawvector(#1,#2){\sarrowlength}}%
      \hskip\@hchannel\hbox{\drawvector(#1,#2){\sarrowlength}}}%
  \d@@blearrtrue\generalsmap(#1,#2)}%
\def\adjarrow(#1,#2){\@findslope(#1,#2)%
  \if@pslope \let\@upordown\raise \else \let\@upordown\lower\fi
  \getch@nnel(#1,#2)\setbox\@arrbox=\hbox{\@upordown\@vchannel
    \rlap{\drawvector(#1,#2){\sarrowlength}}%
      \hskip\@hchannel\hbox{\drawvector(-#1,-#2){\sarrowlength}}}%
  \d@@blearrtrue\generalsmap(#1,#2)}%

% Morphism placement.

% Logic for positioning morphisms on slanted arrows:
% If \lft then
%   \hskip by -\@hmorphdflt
%   if \@pslopetrue then \raise by \@vmorphdflt
%   else \lower by \@vmorphdflt
% Else if \rt then
%   \hskip by \@hmorphdflt
%   if \@pslopetrue then \lower by \@vmorphdflt
%   else \raise by \@vmorphdflt
%
% \@hmorphdflt and \@vmorphdflt defined by \getm@rphposn
% Advance \morphdist by .5\channelwidth if double arrows
%
% Use \@shiftmorph to allow users to move morphisms

% Logic for \@shiftmorph:
% If \rtm@rph then
%   if \hmorphposnrt=0 then hshift by\hmorphposn else hshift by\hmorphposnrt
%   if \vmorphposnrt=0 then vshift by\vmorphposn else vshift by\vmorphposnrt
% Else
%   if \hmorphposnlft=0 then hshift by\hmorphposn else hshift by\hmorphposnlft
%   if \vmorphposnlft=0 then vshift by\vmorphposn else vshift by\vmorphposnlft

\newif\ifrtm@rph
\def\@shiftmorph#1{\hbox{\setbox0=\hbox{$\scriptstyle#1$}%
  \setbox1=\hbox{\hskip\@hm@rphshift\raise\@vm@rphshift\copy0}%
  \wd1=\wd0 \ht1=\ht0 \dp1=\dp0 \box1}}%
\def\@hm@rphshift{\ifrtm@rph
  \ifdim\hmorphposnrt=\z@\hmorphposn\else\hmorphposnrt\fi \else
  \ifdim\hmorphposnlft=\z@\hmorphposn\else\hmorphposnlft\fi \fi}%
\def\@vm@rphshift{\ifrtm@rph
  \ifdim\vmorphposnrt=\z@\vmorphposn\else\vmorphposnrt\fi \else
  \ifdim\vmorphposnlft=\z@\vmorphposn\else\vmorphposnlft\fi \fi}%

\def\addm@rph{\ifx\next\lft\let\temp=\lftmorph\else
  \ifx\next\rt\let\temp=\rtmorph\else\let\temp\relax\fi\fi \temp}%

\def\plnmorph{\dimen1\wd\@arrbox \ifdim\dimen1<\z@ \dimen1-\dimen1\fi
  \vcenter{\box\@arrbox}}%
\def\lftmorph\lft#1{\rtm@rphfalse \setbox0=\@shiftmorph{#1}%
  \if@pslope \let\@upordown\raise \else \let\@upordown\lower\fi
  \llap{\@upordown\@vmorphdflt\hbox to\dimen1{\hss % \dimen1=\wd\@arrbox
    \llap{\box0}\hss}\hskip\@hmorphdflt}\futurelet\next\addm@rph}%
\def\rtmorph\rt#1{\rtm@rphtrue \setbox0=\@shiftmorph{#1}%
  \if@pslope \let\@upordown\lower \else \let\@upordown\raise\fi
  \llap{\@upordown\@vmorphdflt\hbox to\dimen1{\hss
    \rlap{\box0}\hss}\hskip-\@hmorphdflt}\futurelet\next\addm@rph}%

% Get appropriate shifts for morphisms and double lines at various slopes
% Syntax e.g.: \@getshift(1,2){\@hchannel}{\@vchannel}{\channelwidth}%

\def\getm@rphposn(#1,#2){\ifd@@blearr \dimen@\morphdist \advance\dimen@ by
  .5\channelwidth \@getshift(#1,#2){\@hmorphdflt}{\@vmorphdflt}{\dimen@}\else
  \@getshift(#1,#2){\@hmorphdflt}{\@vmorphdflt}{\morphdist}\fi}%

\def\getch@nnel(#1,#2){\ifdim\hchannel=\z@ \ifdim\vchannel=\z@
    \@getshift(#1,#2){\@hchannel}{\@vchannel}{\channelwidth}%
    \else \@hchannel\hchannel \@vchannel\vchannel \fi
  \else \@hchannel\hchannel \@vchannel\vchannel \fi}%

\def\@getshift(#1,#2)#3#4#5{\dimen@ #5\relax
  \&xarg #1\relax \&yarg #2\relax
  \ifnum\&xarg<0 \&xarg -\&xarg \fi
  \ifnum\&yarg<0 \&yarg -\&yarg \fi
  \ifnum\&xarg<\&yarg \&negargtrue \&yyarg\&xarg \&xarg\&yarg \&yarg\&yyarg\fi
  \ifcase\&xarg \or  % There is no case 0
    \ifcase\&yarg    % case 1
      \dimen@i \z@ \dimen@ii \dimen@ \or % case (1,0)
      \dimen@i .7071\dimen@ \dimen@ii .7071\dimen@ \fi \or
    \ifcase\&yarg    % case 2
      \or % case 0,2 wrong
      \dimen@i .4472\dimen@ \dimen@ii .8944\dimen@ \fi \or
    \ifcase\&yarg    % case 3
      \or % case 0,3 wrong
      \dimen@i .3162\dimen@ \dimen@ii .9486\dimen@ \or
      \dimen@i .5547\dimen@ \dimen@ii .8321\dimen@ \fi \or
    \ifcase\&yarg    % case 4
      \or % case 0,2,4 wrong
      \dimen@i .2425\dimen@ \dimen@ii .9701\dimen@ \or\or
      \dimen@i .6\dimen@ \dimen@ii .8\dimen@ \fi \or
    \ifcase\&yarg    % case 5
      \or % case 0,5 wrong
      \dimen@i .1961\dimen@ \dimen@ii .9801\dimen@ \or
      \dimen@i .3714\dimen@ \dimen@ii .9284\dimen@ \or
      \dimen@i .5144\dimen@ \dimen@ii .8575\dimen@ \or
      \dimen@i .6247\dimen@ \dimen@ii .7801\dimen@ \fi \or
    \ifcase\&yarg    % case 6
      \or % case 0,2,3,4,6 wrong
      \dimen@i .1645\dimen@ \dimen@ii .9864\dimen@ \or\or\or\or
      \dimen@i .6402\dimen@ \dimen@ii .7682\dimen@ \fi \fi
  \if&negarg \&tempdima\dimen@i \dimen@i\dimen@ii \dimen@ii\&tempdima\fi
  #3\dimen@i\relax #4\dimen@ii\relax }%

\catcode`\&=4  % Back to alignment tab


% Macros for horizontal and vertical lines and arrows.
% These macros use an idea from Appendix~D, p.~374 of the Texbook.
% Usage: `\mapright^f', `\mapleft', etc.
%        `\mapdown\lft{f}', `\mapup\rt{g}', `\mapdown', etc.

% \toks@ will contain the token sequence that defines the arrow and morphisms;
% ensure that \toks@={\mathop{\vcenter{\smash{horiz. arrow}}}\limits} to start.
\def\generalhmap{\futurelet\next\@generalhmap}%
\def\@generalhmap{\ifx\next^ \let\temp\generalhm@rph\else
  \ifx\next_ \let\temp\generalhm@rph\else \let\temp\m@kehmap\fi\fi \temp}%
\def\generalhm@rph#1#2{\ifx#1^
    \toks@=\expandafter{\the\toks@#1{\rtm@rphtrue\@shiftmorph{#2}}}\else
    \toks@=\expandafter{\the\toks@#1{\rtm@rphfalse\@shiftmorph{#2}}}\fi
  \generalhmap}%
\def\m@kehmap{\mathrel{\smash{\the\toks@}}}%

\def\mapright{\toks@={\mathop{\vcenter{\smash{\drawrightarrow}}}\limits}%
  \generalhmap}%
\def\mapleft{\toks@={\mathop{\vcenter{\smash{\drawleftarrow}}}\limits}%
  \generalhmap}%
\def\bimapright{\toks@={\mathop{\vcenter{\smash{\drawbirightarrow}}}\limits}%
  \generalhmap}%
\def\bimapleft{\toks@={\mathop{\vcenter{\smash{\drawbileftarrow}}}\limits}%
  \generalhmap}%
\def\adjmapright{\toks@={\mathop{\vcenter{\smash{\drawadjrightarrow}}}\limits}%
  \generalhmap}%
\def\adjmapleft{\toks@={\mathop{\vcenter{\smash{\drawadjleftarrow}}}\limits}%
  \generalhmap}%
\def\hline{\toks@={\mathop{\vcenter{\smash{\drawhline}}}\limits}%
  \generalhmap}%
\def\bihline{\toks@={\mathop{\vcenter{\smash{\drawbihline}}}\limits}%
  \generalhmap}%

\def\drawrightarrow{\hbox{\drawvector(1,0){\harrowlength}}}%
\def\drawleftarrow{\hbox{\drawvector(-1,0){\harrowlength}}}%
\def\drawbirightarrow{\hbox{\raise.5\channelwidth
  \hbox{\drawvector(1,0){\harrowlength}}\lower.5\channelwidth
  \llap{\drawvector(1,0){\harrowlength}}}}%
\def\drawbileftarrow{\hbox{\raise.5\channelwidth
  \hbox{\drawvector(-1,0){\harrowlength}}\lower.5\channelwidth
  \llap{\drawvector(-1,0){\harrowlength}}}}%
\def\drawadjrightarrow{\hbox{\raise.5\channelwidth
  \hbox{\drawvector(-1,0){\harrowlength}}\lower.5\channelwidth
  \llap{\drawvector(1,0){\harrowlength}}}}%
\def\drawadjleftarrow{\hbox{\raise.5\channelwidth
  \hbox{\drawvector(1,0){\harrowlength}}\lower.5\channelwidth
  \llap{\drawvector(-1,0){\harrowlength}}}}%
\def\drawhline{\hbox{\drawline(1,0){\harrowlength}}}%
\def\drawbihline{\hbox{\raise.5\channelwidth
  \hbox{\drawline(1,0){\harrowlength}}\lower.5\channelwidth
  \llap{\drawline(1,0){\harrowlength}}}}%

% Vertical arrows are handled differently because there is no \mathop.
% \toks@ will contain the token sequence that defines the arrow and morphisms;
% ensure that \toks@={\vcenter{vertical arrow}} to start.
\def\generalvmap{\futurelet\next\@generalvmap}%
\def\@generalvmap{\ifx\next\lft \let\temp\generalvm@rph\else
  \ifx\next\rt \let\temp\generalvm@rph\else \let\temp\m@kevmap\fi\fi \temp}%
% Prepend or append to \toks@ depending on \rt or \lft.
\toksdef\toks@@=1
\def\generalvm@rph#1#2{\ifx#1\rt % append
    \toks@=\expandafter{\the\toks@
      \rlap{$\vcenter{\rtm@rphtrue\@shiftmorph{#2}}$}}\else % prepend
    \toks@@={\llap{$\vcenter{\rtm@rphfalse\@shiftmorph{#2}}$}}%
    \toks@=\expandafter\expandafter\expandafter{\expandafter\the\expandafter
      \toks@@ \the\toks@}\fi \generalvmap}%
\def\m@kevmap{\the\toks@}%

\def\mapdown{\toks@={\vcenter{\drawdownarrow}}\generalvmap}%
\def\mapup{\toks@={\vcenter{\drawuparrow}}\generalvmap}%
\def\bimapdown{\toks@={\vcenter{\drawbidownarrow}}\generalvmap}%
\def\bimapup{\toks@={\vcenter{\drawbiuparrow}}\generalvmap}%
\def\adjmapdown{\toks@={\vcenter{\drawadjdownarrow}}\generalvmap}%
\def\adjmapup{\toks@={\vcenter{\drawadjuparrow}}\generalvmap}%
\def\vline{\toks@={\vcenter{\drawvline}}\generalvmap}%
\def\bivline{\toks@={\vcenter{\drawbivline}}\generalvmap}%

\def\drawdownarrow{\hbox to5pt{\hss\drawvector(0,-1){\varrowlength}\hss}}%
\def\drawuparrow{\hbox to5pt{\hss\drawvector(0,1){\varrowlength}\hss}}%
\def\drawbidownarrow{\hbox to5pt{\hss\hbox{\drawvector(0,-1){\varrowlength}}%
  \hskip\channelwidth\hbox{\drawvector(0,-1){\varrowlength}}\hss}}%
\def\drawbiuparrow{\hbox to5pt{\hss\hbox{\drawvector(0,1){\varrowlength}}%
  \hskip\channelwidth\hbox{\drawvector(0,1){\varrowlength}}\hss}}%
\def\drawadjdownarrow{\hbox to5pt{\hss\hbox{\drawvector(0,-1){\varrowlength}}%
  \hskip\channelwidth\lower\varrowlength
  \hbox{\drawvector(0,1){\varrowlength}}\hss}}%
\def\drawadjuparrow{\hbox to5pt{\hss\hbox{\drawvector(0,1){\varrowlength}}%
  \hskip\channelwidth\raise\varrowlength
  \hbox{\drawvector(0,-1){\varrowlength}}\hss}}%
\def\drawvline{\hbox to5pt{\hss\drawline(0,1){\varrowlength}\hss}}%
\def\drawbivline{\hbox to5pt{\hss\hbox{\drawline(0,1){\varrowlength}}%
  \hskip\channelwidth\hbox{\drawline(0,1){\varrowlength}}\hss}}%


% Macros for setting commutative diagrams.

% A macro inspired by Ex.~18.46 of the TeXbook.
\def\commdiag#1{\null\,
  \vcenter{\commdiagbaselines
  \m@th\ialign{\hfil$##$\hfil&&\hfil$\mkern4mu ##$\hfil\crcr
      \mathstrut\crcr\noalign{\kern-\baselineskip}
      #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,}%

\def\commdiagbaselines{\baselineskip15pt \lineskip3pt \lineskiplimit3pt }%
% A macro inspired by Francis Borceux's Diagram macros for LaTeX
% (FBORCEUX@BUCLLN11.BITNET).
\def\gridcommdiag#1{\null\,
  \vcenter{\offinterlineskip
  \m@th\ialign{&\vbox to\vgrid{\vss
    \hbox to\hgrid{\hss\smash{$##$}\hss}}\crcr
      \mathstrut\crcr\noalign{\kern-\vgrid}
      #1\crcr\mathstrut\crcr\noalign{\kern-.5\vgrid}}}\,}%

% Default parameters
% Define default heights and widths for arrows using the golden ratio.
% Note that 5:3 (for sline) and 3:2 (for vector) approximate this ratio.
\newdimen\harrowlength \harrowlength=60pt
\newdimen\varrowlength \varrowlength=.618\harrowlength
\newdimen\sarrowlength \sarrowlength=\harrowlength

% Morphism placement
\newdimen\hmorphposn \hmorphposn=\z@
\newdimen\vmorphposn \vmorphposn=\z@
\newdimen\morphdist  \morphdist=4pt

\dimendef\@hmorphdflt 0       % These two dimensions are
\dimendef\@vmorphdflt 2       % defined by \getm@rphposn

\newdimen\hmorphposnrt  \hmorphposnrt=\z@
\newdimen\hmorphposnlft \hmorphposnlft=\z@
\newdimen\vmorphposnrt  \vmorphposnrt=\z@
\newdimen\vmorphposnlft \vmorphposnlft=\z@
\let\hmorphposnup=\hmorphposnrt
\let\hmorphposndn=\hmorphposnlft
\let\vmorphposnup=\vmorphposnrt
\let\vmorphposndn=\vmorphposnlft

% Default grid size for \gridcommdiag
\newdimen\hgrid \hgrid=15pt
\newdimen\vgrid \vgrid=15pt

% Horizontal and vertical distance between double lines and arrows. 
\newdimen\hchannel  \hchannel=0pt
\newdimen\vchannel  \vchannel=0pt
\newdimen\channelwidth \channelwidth=3pt

\dimendef\@hchannel 0         % Defined via the
\dimendef\@vchannel 2         % macro \getch@nnel

\catcode`& = \@oldandcatcode
\catcode`@ = \@oldatcatcode


% Some examples

%\parskip=20pt
%
%The first example:
%$$\commdiag{A&\mapright^f&B&\mapleft^g&C\cr
%\mapdown\lft\psi&\arrow(3,-2)\rt s&\mapup\rt\phi&
%\arrow(-3,2)\lft l&\mapdown\rt\theta\cr
%D&\mapright_h&E&\mapleft_{\int_0^t{\bf A}\,d\sigma}&F\cr}$$
%
%
%Covering homotopy property (Bott and Tu, {\it Differential Forms in
%Algebraic Topology}):
%$$\commdiag{Y&\mapright^f&E\cr \mapdown&\arrow(3,2)\lft{f_t}&\mapdown\cr
%Y\times I&\mapright^{\bar f_t}&X}$$
%
%
%Universal mapping property (Warner, {\it Foundations of Differentiable
%Manifolds and Lie Groups}): $$\varrowlength=20pt
%\commdiag{V\otimes W\cr \mapup\lft\phi&\arrow(3,-1)\rt{\tilde l}\cr
%V\times W&\mapright^l&U\cr}$$
%
%
%A cube (Francis Borceux):
%$$\harrowlength=48pt \varrowlength=48pt \sarrowlength=20pt
%\def\cross#1#2{\setbox0=\hbox{$#1$}%
%  \hbox to\wd0{\hss\hbox{$#2$}\hss}\llap{\unhbox0}}
%\gridcommdiag{&&B&&\mapright^b&&D\cr
%&\arrow(1,1)\lft a&&&&\arrow(1,1)\lft d\cr
%A&&\cross{\hmorphposn=12pt\mapright^c}{\vmorphposn=-12pt\mapdown\lft f}
%&&C&&\mapdown\rt h\cr\cr
%\mapdown\lft e&&F&&\cross{\hmorphposn=-12pt\mapright_j}
%{\vmorphposn=12pt\mapdown\rt g}&&H\cr
%&\arrow(1,1)\lft i&&&&\arrow(1,1)\rt l\cr
%E&&\mapright_k&&G\cr}$$
%
%Zassenhaus's Butterfly Lemma (Lang, {\it Algebra}):
%$$\hgrid=16pt \vgrid=8pt \sarrowlength=32pt
%\def\cross#1#2{\setbox0=\hbox{$#1$}%
%  \hbox to\wd0{\hss\hbox{$#2$}\hss}\llap{\unhbox0}}
%\def\l#1{\llap{$#1$\hskip.5em}}
%\def\r#1{\rlap{\hskip.5em$#1$}}
%\gridcommdiag{&&U&&&&V\cr &&\bullet&&&&\bullet\cr
%&&\sarrowlength=16pt\sline(0,1)&&&&\sarrowlength=16pt\sline(0,1)\cr
%&&\l{u(U\cap V)}\bullet&&&&\bullet\r{(U\cap V)v}\cr
%&&&\sline(2,-1)&&\sline(2,1)\cr
%&&\cross{=}{\sline(0,1)}&&\bullet&&\cross{=}{\sline(0,1)}\cr\cr
%&&\l{^{\textstyle u(U\cap v)}}\bullet&&\cross{=}{\sline(0,1)}&&
% \bullet\r{^{\textstyle(u\cap V)v}}\cr
%&\sline(2,1)&&\sline(2,-1)&&\sline(2,1)&&\sline(2,-1)\cr
%\l{u}\bullet&&&&\bullet&&&&\bullet\r{v}\cr
%&\sline(2,-1)&&\sline(2,1)&&\sline(2,-1)&&\sline(2,1)\cr
%&&\bullet&&&&\bullet\cr &&u\cap V&&&&U\cap v\cr}$$
